import 'dart:async';
import 'dart:math';

import 'package:flutter/foundation.dart';
// import 'package:flutter/material.dart';

///棋盘
class CheckerBoard {
  CheckerBoard({this.columns = 30, double width = 20, double height = 20})
      : assert(columns > 1),
        width = width,
        height = height,
        board = Rectangle(0, 0, width, height),
        cellwidth = width / columns {
    rows = (height / cellwidth).floor();
    rightEnd = (columns - 1) * cellwidth;
    bottomEnd = (rows - 1) * cellwidth;
  }

  // 宽
  final double width;
  // 高
  final double height;

  double rightEnd = 0;
  double bottomEnd = 0;
  //棋盘
  final Rectangle board;

  ///列数
  final int columns;

  /// Cell的宽
  final double cellwidth;

  ///行数
  int rows = 0;

  ///cell列表
  List<Cell>? cells;

  /// 步宽
  double get stepWidth => cellwidth;

  ///row的所有的对应坐标
  List<List<double>> getRowParis() {
    List<List<double>> arr = [];
    for (double i = 0; i < height; i += cellwidth) {
      arr.add([i, width]);
    }
    return arr;
  }

  ///column所有的对应坐标
  List<List<double>> getColumnPairs() {
    List<List<double>> arr = [];
    for (double i = 0; i < width; i += cellwidth) {
      arr.add([i, cellwidth * rows]);
    }
    return arr;
  }

  ///获取圆点
  Point<double> getOrigin() {
    var rd = Random();
    var x = (rd.nextDouble() * (columns - 1)).ceil() * stepWidth;
    var y = (rd.nextDouble() * (rows - 1)).ceil() * stepWidth;
    return Point(x, y);
  }

  ///获取所有的可用点
  List<Point<double>> getAllPoints() {
    var _rows = rows as int;
    List<Point<double>> list = [];
    for (var i = 0; i < _rows; i++) {
      for (var j = 0; j < columns; j++) {
        list.add(Point(j * stepWidth, i * stepWidth));
      }
    }
    return list;
  }
}

///方格
class Cell extends Rectangle<double> {
  Cell(double left, double top, double width, double height)
      : center = Point(left + width / 2, top + height / 2),
        position = Point(left, top),
        super(left, top, width, height);

  ///位置
  Point<double> position;

  ///中心点
  Point<double> center;
}

///蛇
class Snake {
  Snake(Point<double> start, this.width)
      : afterLast = start,
        positions = [start];

  ///宽度
  final width;

  ///位置
  List<Point<double>> positions;

  ///上一个预留位置
  Point afterLast;

  ///移动到某个位置
  void moveTo(Point<double> p) {
    // 判断是否是其身体的方块
    // 判断是否是按着的方块
    print('move to $p');
    positions.insert(0, p);
    afterLast = positions.removeLast();
  }

  ///吃掉
  void eatIt(Point<double> p) {
    print('吃球');
    positions.insert(0, p);
  }

  ///渲染
  List<Cell> render() {
    return positions.map((p) => Cell(p.x, p.y, width, width)).toList();
  }

  ///长度
  int get length => positions.length;

  ///头部
  Cell get head => Cell(positions.first.x, positions.first.y, width, width);
}

///方向枚举
// enum direction { left, top, right, bottom }
enum direction { west, north, east, south }
enum GameState { ready, lose, runing, win }

///游戏控制器
class GameController extends ChangeNotifier {
  GameController({this.board, this.snake, this.speed = 500}) {
    ball = nextBall();
    initPosition();
  }

  ///速度
  int speed;

  bool auto = false;

  ///timer驱动器
  Timer? _timer;

  ///棋盘
  CheckerBoard? board;

  ///蛇
  Snake? snake;

  ///下一个球
  Point<double>? ball;

  ///是否赢了
  GameState state = GameState.ready;

  ///下一次方向
  direction? _nextDirection;

  ///方向
  direction _direction = direction.north;

  // /初始化位置
  initPosition() {
    stop();
    state = GameState.ready;
    snake?.positions = [(board as CheckerBoard).getOrigin()];
    ball = nextBall();
    notifyListeners();
  }

  ///开始
  void start() {
    stop();
    print('游戏开始，向$_direction移动');
    // 使用timer来驱动
    _timer = Timer.periodic(Duration(milliseconds: speed), (timer) {
      update();
    });
  }

  ///结束
  stop() {
    _timer?.cancel();
  }

  ///更新
  update() {
    if (_nextDirection != null) {
      _direction = _nextDirection as direction;
    }
    assert(board != null);
    assert(snake != null);
    var _snake = snake as Snake;
    var nextPoint = _getNextPoint();
    var cellwidth = (board as CheckerBoard).cellwidth;
    var nextCell = Cell(nextPoint.x, nextPoint.y, cellwidth, cellwidth);

    // 如果撞向自身也为数
    if (_snake.positions.contains(nextPoint)) {
      stop();
      state = GameState.lose;
      print('失败，游戏结束');
      // 通知listeners
      notifyListeners();
      return;
    }

    //如果没有逸出
    if ((board as CheckerBoard).board.containsRectangle(nextCell)) {
      // 是否吃掉球
      if (nextCell.position == ball) {
        _snake.eatIt(ball as Point<double>);
        if (state != GameState.win) {
          // 如果没有赢则生成下一个
          ball = nextBall();
        }
      } else {
        // 继续移动
        _snake.moveTo(nextPoint);
        // 是否自己旋转
        if (auto && isToEnd(_snake.head.position)) {
          turnRight();
        }
      }

      // 判断输赢
      if (ball == null) {
        stop();
        state = GameState.win;
        print('恭喜，赢得游戏');
      }
    } else {
      // 不包含则停止且报错
      stop();
      state = GameState.lose;
      print('游戏停止，失败');
    }
    //通知listener
    notifyListeners();
  }

  // 更改方向
  void turnWest() {
    if (_direction == direction.east) return;
    _nextDirection = direction.west;
    print('turnWest');
  }

  void turnNorth() {
    if (_direction == direction.south) return;
    _nextDirection = direction.north;
    print('turnNorth');
  }

  void turnEast() {
    if (_direction == direction.west) return;
    _nextDirection = direction.east;
    print('turnEast');
  }

  void turnSouth() {
    if (_direction == direction.north) return;
    _nextDirection = direction.south;
    print('turnSouth');
  }

  ///向右移动
  void turnRight() {
    print('turn right');
    switch (_direction) {
      case direction.west:
        _nextDirection = direction.north;
        break;
      case direction.north:
        _nextDirection = direction.east;
        break;
      case direction.east:
        _nextDirection = direction.south;
        break;
      default:
        _nextDirection = direction.west;
    }
  }

  ///向左移动
  void turnLeft() {
    print('turn left');
    switch (_direction) {
      case direction.west:
        _nextDirection = direction.south;
        break;
      case direction.north:
        _nextDirection = direction.west;
        break;
      case direction.east:
        _nextDirection = direction.north;
        break;
      default:
        _nextDirection = direction.east;
    }
  }

  ///是否到了顶头
  bool isToEnd(Point<double> p) {
    var _board = board as CheckerBoard;
    switch (_direction) {
      case direction.west:
        return p.x == 0;
      case direction.north:
        return p.y == 0;
      case direction.east:
        return p.x == _board.rightEnd;
      case direction.south:
        return p.y == _board.bottomEnd;
    }
  }

  /// 生成下一个球
  Point<double>? nextBall() {
    var _board = board as CheckerBoard;
    var _snake = snake as Snake;
    var arr = List.from(_board.getAllPoints());
    arr.removeWhere((element) => _snake.positions.contains(element));
    if (arr.length == 0) {
      return null;
    } else {
      return arr[Random().nextInt(arr.length)];
    }
  }

  ///获取下一步的位置
  Point<double> _getNextPoint() {
    assert(snake != null);
    assert(board != null);
    var currentPoint = snake?.head.position as Point<double>;
    var stepwidth = (board as CheckerBoard).stepWidth;
    var x, y;
    switch (_direction) {
      case direction.west:
        x = currentPoint.x - stepwidth;
        y = currentPoint.y;
        break;
      case direction.north:
        x = currentPoint.x;
        y = currentPoint.y - stepwidth;
        break;
      case direction.east:
        x = currentPoint.x + stepwidth;
        y = currentPoint.y;
        break;
      default:
        x = currentPoint.x;
        y = currentPoint.y + stepwidth;
    }
    return Point(x, y);
  }

  ///重置
  reset() {
    initPosition();
  }
}
